/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.services.project;

import cn.easyplatform.contexts.Contexts;
import cn.easyplatform.dos.EnvDo;
import cn.easyplatform.dos.UserDo;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.admin.ServiceVo;
import cn.easyplatform.services.ISessionManager;
import cn.easyplatform.type.DeviceType;
import cn.easyplatform.type.StateType;
import cn.easyplatform.type.UserType;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;


/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
class SessionManagerImpl implements ISessionManager {

    private SessionDAO sessionDAO;
    private ProjectService ps;

    SessionManagerImpl(ProjectService ps) {
        this.ps = ps;
        sessionDAO = ((DefaultSessionManager) ((DefaultSecurityManager) SecurityUtils.getSecurityManager()).getSessionManager()).getSessionDAO();
        Collection<Session> collection = sessionDAO.getActiveSessions();
        for (Session sess : collection) {
            UserDo user = (UserDo) sess.getAttribute(CommandContext.USER_KEY);
            if (user != null) {
                EnvDo env = (EnvDo) sess.getAttribute(CommandContext.USER_ENV);
                if (env != null && Strings.equals(env.getId(), ps.getId()))
                    sessionDAO.delete(sess);
            }
        }
    }

    private MySession[] getSessions(boolean authorized) {
        Collection<Session> collection = sessionDAO.getActiveSessions();
        List<MySession> sessions = new ArrayList<>();
        for (Session sess : collection) {
            UserDo user = (UserDo) sess.getAttribute(CommandContext.USER_KEY);
            boolean flag = user != null && user.getState() != StateType.STOP && user.getDeviceType() != DeviceType.JOB;
            if (authorized)//仅已登陆用户
                flag = flag && user.getType() < UserType.TYPE_OAUTH;
            if (flag) {
                EnvDo env = (EnvDo) sess.getAttribute(CommandContext.USER_ENV);
                if (env != null && Strings.equals(env.getId(), ps.getId()))
                    sessions.add(new MySession(sess, user));
            }
        }
        return sessions.toArray(new MySession[sessions.size()]);
    }

    @Override
    public UserDo getUser(String id) {
        MySession[] sessions = getSessions(true);
        for (MySession session : sessions) {
            if (id.equals(session.userDo.getId()))
                return session.userDo;
        }
        return null;
    }

    @Override
    public Collection<UserDo> getUsers() {
        MySession[] sessions = getSessions(true);
        List<UserDo> onlineUsers = new ArrayList<>(sessions.length);
        for (MySession session : sessions)
            onlineUsers.add(session.userDo);
        return onlineUsers;
    }

    @Override
    public int checkUser(UserDo user) {
        UserDo other = null;
        if (user.getSessionId() != null) {
            Session session = sessionDAO.readSession(user.getSessionId());
            other = (UserDo) session.getAttribute(CommandContext.USER_KEY);
            if (other != null)// 同一个用户
                return UserDo.ONLINE_SELF;
        } else {
            MySession[] sessions = getSessions(true);
            for (MySession session : sessions) {
                if (session.userDo.getType() == user.getType() && session.userDo.getId().equals(user.getId()))// 同用户，不同会话
                    return UserDo.ONLINE_OTHER;
            }
        }
        // 用户不存在
        return UserDo.ONLINE_NONE;
    }

    @Override
    public void setState(Serializable sessionId, int state) {
        if (sessionId != null) {
            Session session = sessionDAO.readSession(sessionId);
            UserDo user = (UserDo) session.getAttribute(CommandContext.USER_KEY);
            if (user != null) {
                user.setState(state);
                session.setAttribute(CommandContext.USER_KEY, user);
            }
        } else if (state == StateType.PAUSE) {
            UserDo currentUser = Contexts.getCommandContext().getUser();
            MySession[] sessions = getSessions(false);
            for (MySession sess : sessions) {
                UserDo user = sess.userDo;
                if (!user.getId().equals(currentUser.getId())) {
                    user.setState(state);
                    sess.session.setAttribute(CommandContext.USER_KEY, user);
                }
            }
        }
    }

    @Override
    public List<ServiceVo> getServices() {
        MySession[] sessions = getSessions(false);
        List<ServiceVo> services = new ArrayList<>(sessions.length);
        for (MySession sess : sessions) {
            UserDo user = sess.userDo;
            ServiceVo s = new ServiceVo(user.getId(), user.getName(),
                    user.getType(), user.getState());
            s.setValue("ip", user.getIp());
            s.setValue("time", user.getLoginDate());
            if (user.getOrg() == null)
                s.setValue("org", "");
            else
                s.setValue("org", user.getOrg().getId() + "(" + user.getOrg().getName() + ")");
            if (user.getRoles() == null)
                s.setValue("role", "");
            else
                s.setValue("role", Arrays.toString(user.getRoles()));
            s.setValue("client", user.getDeviceType().getName());
            s.setValue("session", user.getSessionId());
            s.setValue("timeout", sess.session.getTimeout());
            s.setValue("lastAccessTime", sess.session.getLastAccessTime());
            services.add(s);
        }
        return services;
    }

    @Override
    public int getCount(UserDo user) {
        MySession[] sessions = getSessions(true);
        if (user == null)
            return sessions.length;
        List<String> sharedUsers = new ArrayList<>();
        int count = 0;
        for (MySession session : sessions) {
            UserDo that = session.userDo;
            if (that.getType() != UserType.TYPE_ADMIN) {
                if (that.getType() == UserType.TYPE_SHARE) {
                    if (that.getId().equals(user.getId())) {
                    } else if (!sharedUsers.contains(that.getId())) {
                        sharedUsers.add(that.getId());
                        count++;
                    }
                } else
                    count++;
            }
        }
        return count;
    }

    class MySession {

        Session session;

        UserDo userDo;

        public MySession(Session session, UserDo userDo) {
            this.session = session;
            this.userDo = userDo;
        }
    }
}
